/**
 * Copyright 2005 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.edi.utils
{
	import flash.net.ObjectEncoding;
	import flash.net.registerClassAlias;
	import flash.utils.ByteArray;
	import flash.utils.describeType;
	import flash.utils.getDefinitionByName;
	import flash.utils.getQualifiedClassName;
	
	/**
	 * 对象工具类
	 * @version v1.0
	 * 
	 */	
	public class BOPObjectUtils
	{
		/**
		 * @private
		 */
		public function BOPObjectUtils() {
		}
		
		/**
		 * 判断Object对象中的元素是否为简单类型(简单类型包含number,string,boolean,object四种)
		 *
		 * @param 源对象
		 * @return 如果object中所有的元素均为简单类型,则返回true;否则返回false.
		 */
		public static function isSimple(object:Object):Boolean {
			switch (typeof(object)) {
				case "number":
				case "string":
				case "boolean":
					return true;
				case "object":
					return (object is Date) || (object is Array);
			}
			
			return false;
		}
		
		/**
		 * 返回object对象中所有的key.
		 * @param object
		 * @return 
		 * 
		 */		
		public static function getKeys(object:Object):Array {
			var result:Array = [];
			
			for (var k:*in object) {
				result.push(k);
			}
			return result;
		}
		
		/**
		 * 获得object对象中的元素个数. 
		 * @param object
		 * @return 
		 * 
		 */		
		public static function getNumProperties(object:Object):int {
			var result:int = 0;
			
			for (var p:String in object) {
				result++;
			}
			return result;
		}
		
		/**
		 * 获得object对象中所有的key 
		 * @param object
		 * @return 
		 * 
		 */		
		public static function getProperties(object:Object):Array {
			var result:Array = [];
			
			for each (var p:Object in object) {
				result.push(p);
			}
			return result;
		}
		
		/**
		 * 复制object对象 
		 * @param object
		 * @return 
		 * 
		 */		
		public static function clone(object:Object):* {
			var byteArray:ByteArray = new ByteArray();
			byteArray.writeObject(object);
			byteArray.position = 0;
			return byteArray.readObject();
		}
		
		/**
		 * 复制object对象的深度复制
		 * @param object
		 * @return 
		 * 
		 */		
		public static function deptClone(object:Object):* {
			var qClassName:String = getQualifiedClassName(object);
            var objectType:Class = getDefinitionByName(qClassName) as Class;
            registerClassAlias(qClassName, objectType);
            var copier : ByteArray = new ByteArray();
            copier.writeObject(object);
            copier.position = 0;
			return copier.readObject();
		}
		
		/**
		 * 暂不使用 
		 * @param object
		 * @param clazz
		 * @return 
		 * 
		 */		
		public static function toInstance(object:Object, clazz:Class):* {
			var bytes:ByteArray = new ByteArray();
			bytes.objectEncoding = ObjectEncoding.AMF0;
			
			// Find the objects and byetArray.writeObject them, adding in the
			// class configuration variable name -- essentially, we're constructing
			// and AMF packet here that contains the class information so that
			// we can simplly byteArray.readObject the sucker for the translation
			
			// Write out the bytes of the original object
			var objBytes:ByteArray = new ByteArray();
			objBytes.objectEncoding = ObjectEncoding.AMF0;
			objBytes.writeObject(object);
			
			// Register all of the classes so they can be decoded via AMF
			var typeInfo:XML = describeType(clazz);
			var fullyQualifiedName:String = typeInfo.@name.toString().replace(/::/, ".");
			registerClassAlias(fullyQualifiedName, clazz);
			
			// Write the new object information starting with the class information
			var len:int = fullyQualifiedName.length;
			bytes.writeByte(0x10); // 0x10 is AMF0 for "typed object (class instance)"
			bytes.writeUTF(fullyQualifiedName);
			// After the class name is set up, write the rest of the object
			bytes.writeBytes(objBytes, 1);
			
			// Read in the object with the class property added and return that
			bytes.position = 0;
			
			// This generates some ReferenceErrors of the object being passed in
			// has properties that aren't in the class instance, and generates TypeErrors
			// when property values cannot be converted to correct values (such as false
			// being the value, when it needs to be a Date instead).  However, these
			// errors are not thrown at runtime (and only appear in trace ouput when
			// debugging), so a try/catch block isn't necessary.  I'm not sure if this
			// classifies as a bug or not... but I wanted to explain why if you debug
			// you might seem some TypeError or ReferenceError items appear.
			var result:* = bytes.readObject();
			return result;
		}
		
		/**
		 * 判断object对象的类型是否为给定的参数
		 *
		 *
		 * @param object 源对象
		 * @param clazz 对象的类型
		 * @return object的类型为clazz的时候,返回true;否则返回false.
		 */
		public static function isExplicitInstanceOf(object:Object, clazz:Class):Boolean {
			var c:Class = ClassUtils.forInstance(object);
			return (c == clazz);
		}
		
		/**
		 * 返回object对象的类型 
		 * @param object
		 * @return 
		 * 
		 */		
		public static function getClassName(object:Object):String {
			return ClassUtils.getName(ClassUtils.forInstance(object));
		}
		
		/**
		 * 获得object对象的全路径类名 
		 * @param object
		 * @param replaceColons
		 * @return 
		 * 
		 */		
		public static function getFullyQualifiedClassName(object:Object, replaceColons:Boolean = false):String {
			return ClassUtils.getFullyQualifiedName(ClassUtils.forInstance(object), replaceColons);
		}
		
		/**
		 * 返回object对象的指针 
		 * @param instance
		 * @return 
		 * 
		 */		
		public static function getIterator(instance:Object):IIterator {
			var name:String = getFullyQualifiedClassName(instance);
			var keys:XMLList; 
			
			if((keys = ObjectIterator.getDescription(name)) == null) {
				var description:XML = describeType(instance);
				keys = description.descendants("variable").@name 
					+ description.descendants("accessor").(@access == "readwrite").@name;
				ObjectIterator.setDescription(name, keys);
			}
			
			return new ObjectIterator(instance, keys);;
		}

	}
}